/**
 * Copyright (c) 2016-2020 https://github.com/zhaohuatai
 *
 * contact 824069438@qq.com
 *  
 */
package org.zfes.snowy.base.dao.params;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.util.StringUtils;
import org.zfes.snowy.base.dao.enums.QueryType;
import org.zfes.snowy.core.consts.FrameConst;
import org.zfes.snowy.core.exceptions.AppRuntimeException;
import org.zfes.snowy.core.util.ZObjectUtil;
import org.zfes.snowy.core.util.ZStrUtil;

public  class ParamMap extends HashMap<String,Object> {
	private static final long serialVersionUID = 1L;
	
	public ParamMap(){
		super();
	}
	
	private ParamMap(QueryType type){
		if(type!=null){
			this.put(FrameConst.QUERYTYPE_KEY, type);
		}
	}
	public static ParamMap newPm(QueryType type){
		return new ParamMap(type);
	}
	public static ParamMap newSimpleMap(){
		return newPm(null);
	}
	public static ParamMap newNonePm(){
		return newPm(QueryType.S_NO_NC);
	}
	public static ParamMap newFullPm(){
		return newPm(QueryType.S_O_C);
	}
	
	
	public static ParamMap newNonePm(String key,Object value){
		if(ZStrUtil.hasText(key)){
			throw new AppRuntimeException("参数 key 不允许为空");
		}
		ParamMap pramMap=ParamMap.newNonePm();
		pramMap.put(key, value);
		return pramMap;
	}
	
	public static List<ParamMap> newpmList(){
		return new ArrayList<ParamMap>();
	}
	public static ParamMap[] newpms(int size){
		ParamMap[] paramMaps=new ParamMap[size];
		return paramMaps;
	}
	
	public ParamMap addParam(String key,Object value){
		if(!ZStrUtil.hasText(key)){
			throw new AppRuntimeException("参数 key 不允许为空");
		}
		if(value!=null){
			this.put(key, ZStrUtil.trimToNull(value));
		}
		return this;
	}
	public ParamMap updateParam(String key, Object value){
		return addParam(key,value);
	}
	
	public ParamMap addParams(Map<String,Object> params){
		Iterator<Map.Entry<String,Object>> entries = params.entrySet().iterator();
		while (entries.hasNext()) {
		    Map.Entry<String,Object> entry = entries.next();
		    String key=entry.getKey();
		    Object value=entry.getValue();
		    this.addParam( key, value);
		}
		return this;
	}
	//-------------------------------------------------------------------------
//	 public Optional<Object> get(String key) {
//		return  getObj( key);
//	 }
	public Optional<Object> getObj(String key) {
		if(this.isEmpty()||!ZStrUtil.hasText(key)){
			return Optional.empty();
		}
		Object res=this.get(key);
		if(res==null){
			return Optional.empty();
		}
		if(res instanceof String&&("null".equals(res)||"undefined".equals(res))){
			return Optional.empty();
		}
		return Optional.ofNullable(res);
	}
	
	public Optional<String> getStr(String key) {
		Optional<String> str=ZObjectUtil.castStr(getObj( key));
		if(str.isPresent()&&ZStrUtil.hasText(str.get())){
			return str;
		}
		return Optional.empty();
	}
	
	public Optional<Integer> getInt(String key) {
		return ZObjectUtil.toInteger(getObj( key));
	}
	
	public Optional<Long> getLong(String key) {
		return ZObjectUtil.toLong(getObj( key));
	}
	public Optional<Double> getDouble(String key) {
		return ZObjectUtil.toDouble(getObj( key));
	}
	
	public Optional<Float> getFloat(String key) {
		return ZObjectUtil.toFloat(getObj( key));
	}
	public Optional<Short> getShort(String key) {
		return ZObjectUtil.toShort(getObj( key));
	}
	
	public Optional<Byte> getByte(String key) {
		return ZObjectUtil.toByte(getObj( key));
	}
	
	public Optional<Date> getDate(String key) {
		return ZObjectUtil.toDate(getObj( key));
	}
	public Optional<BigDecimal> getBigDecimal(String key) {
		return ZObjectUtil.toBigDecimal(getObj( key));
	}
	public Optional<byte[]> getByteArray(String key) {
		return ZObjectUtil.castByteArray(getObj( key));
	}
	
	public Optional<Boolean>  getBoolean(String key){
		return ZObjectUtil.toBoolean(getObj( key));
	}
	
	///-----------------------------------------

	public static ParamMap[] toParamArray(List<ParamMap> list){
		if(list==null||list.size()==0){
			return new ParamMap[0];
		}
		ParamMap[] paramMaps=list.stream().toArray(size->new ParamMap[size]);
		return paramMaps;
	}
	
	public static ParamMap filterParam(Map<String,Object> params){
		return filterParam(params,QueryType.S_O_C);
	}
	
	public static ParamMap filterParam(Map<String,Object> params,QueryType qt){
		ParamMap pm=ParamMap.newFullPm();
		if(params!=null){
			Iterator<Map.Entry<String,Object>> entries = params.entrySet().iterator();
			while (entries.hasNext()) {
				
			    Map.Entry<String,Object> entry = entries.next();
			    
			    String key= ZStrUtil.trimToNull(entry.getKey());
			    if(!StringUtils.hasText(key)){
			    	 continue;
			    }
			    Object param= ZStrUtil.trimToNull(entry.getValue());
			    if(param!=null){
			    	pm.put(key, param);
				}
			}
		}

		
		String  page = (String) params.get(FrameConst.PAGE_OFFSET.PAGE);
		String  rows = (String) params.get(FrameConst.PAGE_OFFSET.ROWS);
		
		rows=(rows==null||rows.isEmpty())?(""+FrameConst.PAGE_OFFSET.DEFAULT_ROWS):rows;
		page=(page==null||page.isEmpty())?(""+FrameConst.PAGE_OFFSET.DEFAULT_PAGE):page;
		
		long current_page = Long.valueOf(page);
		long current_row = Long.valueOf(rows);
		
		if(current_page<1){current_page=1L;}
		if(current_row<0){current_row = FrameConst.PAGE_OFFSET.DEFAULT_ROWS;}
		if(current_row>FrameConst.PAGE_OFFSET.MAX_ROWS){current_row = FrameConst.PAGE_OFFSET.MAX_ROWS;}
		
//		针对不同的数据库分页模式
		pm.put(FrameConst.PAGE_OFFSET.START, ((current_page-1)*current_row));
		pm.put(FrameConst.PAGE_OFFSET.END, (current_page*current_row));
		pm.put(FrameConst.PAGE_OFFSET.SIZE, current_row);
		pm.put(FrameConst.PAGE_OFFSET.PAGE, current_page);
		pm.put(FrameConst.PAGE_OFFSET.ROWS, current_row);
		
		String  sort = (String) params.get(FrameConst.PAGE_OFFSET.SORT);
		String  order = (String) params.get(FrameConst.PAGE_OFFSET.ORDER);
		if(sort!=null&& !sort.isEmpty()){
			if(order==null||order.isEmpty()){
				order = FrameConst.PAGE_OFFSET.ASC;
			}
			if(!order.trim().equalsIgnoreCase(FrameConst.PAGE_OFFSET.ASC.trim())&&!order.trim().equalsIgnoreCase(FrameConst.PAGE_OFFSET.DESC.trim())){
				order = FrameConst.PAGE_OFFSET.ASC;
			}
			pm.put(FrameConst.PAGE_OFFSET.SORT, sort);
			pm.put(FrameConst.PAGE_OFFSET.ORDER,order);
		}else{
			pm.remove(FrameConst.PAGE_OFFSET.SORT);
			pm.remove(FrameConst.PAGE_OFFSET.ORDER);
		}
		if(qt!=null){
			pm.put(FrameConst.QUERYTYPE_KEY, qt);
		}
		return pm;
	}
	
//----------------------------------------------------------------
	
	public Long getPage() {
		return (Long) this.get(FrameConst.PAGE_OFFSET.PAGE);
	}
	public Long getRows() {
		return (Long) this.get(FrameConst.PAGE_OFFSET.ROWS);
	}
	public Long getSize() {
		return (Long) this.get(FrameConst.PAGE_OFFSET.SIZE);
	}
	public Long getStart() {
		return (Long) this.get(FrameConst.PAGE_OFFSET.START);
	}
	public Long getEnd() {
		return (Long) this.get(FrameConst.PAGE_OFFSET.END);
	}
	public String getSort() {
		return (String) this.get(FrameConst.PAGE_OFFSET.SORT);
	}
	public String getOrder() {
		return (String) this.get(FrameConst.PAGE_OFFSET.ORDER);
	}
	public QueryType getQueryType() {
		return  (QueryType) this.get(FrameConst.QUERYTYPE_KEY);
	}
	public ParamMap setPage(Long page) {
		this.put(FrameConst.PAGE_OFFSET.PAGE, page);
		return this;
	}
	public ParamMap setRows(Long rows) {
		this.put(FrameConst.PAGE_OFFSET.ROWS, rows);
		return this;
	}
	public ParamMap setSize(Long size) {
		this.put(FrameConst.PAGE_OFFSET.SIZE, size);
		return this;
	}
	public ParamMap setStart(Long start) {
		this.put(FrameConst.PAGE_OFFSET.START, start);
		return this;
	}

	public ParamMap setEnd(Long end) {
		this.put(FrameConst.PAGE_OFFSET.END, end);
		return this;
	}
	public ParamMap setOrder(String order) {
		if(order==null||order.isEmpty()){
			order = FrameConst.PAGE_OFFSET.ASC;
		}
		if(!order.equalsIgnoreCase(FrameConst.PAGE_OFFSET.ASC)&&!order.equalsIgnoreCase(FrameConst.PAGE_OFFSET.DESC)){
			order = FrameConst.PAGE_OFFSET.ASC;
		}
		this.put(FrameConst.PAGE_OFFSET.ORDER, order);
		return this;
	}
	public ParamMap setSort(String sort) {
		this.put(FrameConst.PAGE_OFFSET.SORT, sort);
		return this;
	}
	public ParamMap setQueryType(QueryType qt) {
		this.put(FrameConst.QUERYTYPE_KEY, qt);
		return this;
	}

}
